/**
 * #info
 *@author   wuwg
 *@createTime    2018/03/20
 *@updateTime    2018/06/23
 *@description   tree-store   树的存储
 */
/**
 * @name TreeStore
 */
const treeStore = {
  props: {
    /**
     * #property
     *
     * @name    checkedIds
     * @cn      默认选中的树节点的id
     * @group   数据
     * @type    array
     * @description 默认选中的树节点的id
     */
    checkedIds: {
      type: Array,
      default () {
        return []
      }
    },
    /**
     * #property
     *
     * @name    activeId
     * @cn      默认激活的树节点
     * @group   数据
     * @type    string,number
     * @description  默认激活的树节点
     */
    activeId: {
      type: [String, Number],
      default: ''
    },
    /**
     * #property
     *
     * @name    allowDrop
     * @cn      检测拖拽的节点是否可以放置到当前目标的函数
     * @group   数据
     * @type    function
     * @bind    must
     * @description  检测拖拽的节点是否可以放置到当前目标的函数，返回true或者false, true为可以放置，false为不可放置，默认永远返回true.
     */
    allowDrop: {
      type: Function,
      default: function (dragNode, dropNode) {
        return true
      }
    }
  },
  data () {
    return {
      // 关于过滤
      // currentFilter: this.filter,
      // currentFilterTimer: null,
      // currentFilterDelay: this.filterDelay,
      //  过滤出来的节点
      // currentFilterNodes: [],
      // currentFilterIndex: 0,
      // 激活的id
      currentActiveId: this.activeId,
      // 激活的节点
      currentActiveTreeNode: null,
      // 选中的node的key
      currentCheckedIds: this.checkedIds,
      // /******************
      //  *
      //  * 关于拖拽的数据存储
      //  *
      //  ******************/
      // // 被拖动的节点
      // dragNode: {},
      // // 放置目标的节点
      // dropNode: {},
      // // 拖放中经过的节点
      // hoverNode: {},
      // // 是否正在拖拽
      // dragging: false,
      // // 目标应该被放置的位置
      // targetPostion: 'middle',//  top, bottom
      // // 拖动
      // // 被拖动元素预览图
      // treeDragPreview: {},
      // // 被拖动元素预览图的style
      // treeDragPreviewStyle: {},
      // // 拖动的占位线
      // treeDragLine: {},
      // // 拖动的占位线的样式
      treeDragLineStyle: {
        display: 'none',
        top: 0,
        left: 0,
        width: 0
      },
      /*
       * // 拖动的元素的内容展示
       * treeDragContent: null,
       * // 拖动的元素的内容展示的样式
       */
      treeDragContentStyle: {
        display: 'none',
        top: 0,
        left: 0
      },
      // // 自动完成的源数据
      completeOriginData: []
      /*
       * // 源数据变动定时器（防抖函数）
       * completeOriginDataTimer:null,
       * // 获得焦点的树节点
       * focusTreeNode:null
       */
    }
  },
  watch: {
    // 监听激活的id的变化
    activeId (newValue, oldValue) {
      this.setActiveId(newValue)
    },
    checkedIds (newValue, oldValue) {
      this.currentCheckedIds = newValue
      // 以下是判断checkedIds变化后触发tree-node事件去判断选中状态, 原来是全量触发事件. 改成增量了
      const added = newValue.filter(i => !oldValue.includes(i))
      const removed = oldValue.filter(i => !newValue.includes(i))
      const consumer = (id) => {
        const treeNode = this.getTreeNode(id)
        if (treeNode) {
          treeNode.$emit('changeChecked')
        }
      }
      added.forEach(consumer)
      removed.forEach(consumer)
      this.$emit('watch-update', true, this.currentActiveTreeNode)
    }
  },
  methods: {
    // 重置store
    // resetStore (isEmpty) {
    //   /*
    //    * 激活的节点
    //    */
    //   this.currentActiveTreeNode = null
    //   // 选中的node, 使用对象相比数组性能更好，数组需要循环遍历。
    //   // this.currentCheckedNodes = {}
    //   /*
    //    * 当前所有的数节点
    //    * // http://code.thunisoft.com/artery/artery-ui/issues/82
    //    */
    //   this.allTreeNodeData = {}
    // },
    // 隐藏线和内容提示框
    hideTreeDragLineAndContent () {
      this.treeDragLineStyle.display = 'none'
      this.treeDragContentStyle.display = 'none'
    },
    // 计算拖动线的位置
    computedTreeDragLinePostion (event, isShow) {
      const _style = { top: 0, width: 0, left: 0 }
      let _isShow = isShow
      if (_isShow) {
        //  _target  =>head
        const _target = this.hoverNode.$refs.treeNodeHeadRef
        //  _target  =>head=> name
        const _targetNodeName = this.hoverNode.$refs.treeNodeNameRef
        // 如果是当前的目标，那么就不需要处理
        if (_target == this.startTarget) {
          _isShow = false
          this.isSelf = true
        } else {
          this.isSelf = false
          const _targetRect = _target.getBoundingClientRect()
          const _targetSpanRect = _targetNodeName.getBoundingClientRect()
          /**
           * @name   处理当前鼠标相对于屏幕的位置
           * @start
           * @time    2018-06-02
           * @author    wuwg
           * @description  这里的处理相当精妙，
           *    1.结合css的absolute定位，我们能通过event.pageX，event.pageY快速得出这个元素的top和left位置，
           *    2.结合 getBoundingClientRect()能快速得到此元素当前状态相对屏幕的位置。
           */
          // 设置被拖动元素预览图的style
          this.setTreeDragPreviewStyle(event)
          // 获取被拖动元素预览图的rect
          const _targetDragPreviewRect = this.treeDragPreview.getBoundingClientRect()
          // 鼠标位置
          const _eventOffset = {
            top: _targetDragPreviewRect.top,
            left: _targetDragPreviewRect.left
          }
          /**
           * @name   处理当前鼠标相对于屏幕的位置
           * @end
           */
          const _top = Math.abs(_targetSpanRect.top - _targetRect.top)
          const _bottom = _top + _targetSpanRect.height
          _isShow = false
          this.targetPostion = 'middle'
          // 如果小于 _top
          if (_eventOffset.top - _targetRect.top < _top) {
            _style.top = _targetRect.top
            // 显示线
            _isShow = true
            // 目标位置
            this.targetPostion = 'top'
          }
          // 如果小于 _bottom
          if (_eventOffset.top - _targetRect.top > _bottom) {
            _style.top = _targetRect.bottom
            // 显示线
            _isShow = true
            // 目标位置
            this.targetPostion = 'bottom'
          }
          _style.left = _targetSpanRect.left
          _style.width = _targetRect.width - (_targetSpanRect.left - _targetRect.left)
        }
      }
      // 设置拖动的占位线的样式
      this.setTreeDragLineStyle(_style, _isShow)
    },
    // 设置拖动的占位线的样式
    setTreeDragLineStyle (style, isShow) {
      this.treeDragLineStyle = {
        display: isShow ? 'block' : 'none',
        top: style.top + 'px',
        left: style.left + 'px',
        width: style.width + 'px'
      }
    },
    // 拖动的元素的内容展示的样式
    setTreeDragContentStyle (event, isShow) {
      this.treeDragContentStyle = {
        top: (event.pageY + 5) + 'px',
        left: (event.pageX + 25) + 'px',
        display: isShow ? 'block' : 'none'
      }
    },
    // 被拖动元素预览图的style
    setTreeDragPreviewStyle (event) {
      // 为了及时响应，这里不能用vue的style绑定，因为有延迟。
      this.treeDragPreview.style = [
        'top:' + event.pageY + 'px',
        'left:' + event.pageX + 'px'
      ].join(';')
    },
    // 移动数据
    moveData () {
      const allowDrap = this.allowDrop(this.dragNode, this.dropNode)
      if (!allowDrap) {
        return false
      }

      // 被拖动数据index属性
      const _dragIndexArray = this.dragNode.index.split('-')
      const _dragIndexLen = _dragIndexArray.length
      // 当前的index
      const dragIndex = ~~_dragIndexArray[_dragIndexLen - 1]
      // 当前拖动数据的兄弟节点数组
      let dragDataSiblings = this.currentData
      // 循环  dragIndexArray
      _dragIndexArray.forEach((item, index) => {
        if (index != _dragIndexLen - 1) {
          dragDataSiblings = dragDataSiblings[item].children
        }
      })

      // 被放置数据index属性
      const _dropIndexArray = this.dropNode.index.split('-')
      const _dropIndexLen = _dropIndexArray.length
      // 当前的index
      const dropIndex = ~~_dropIndexArray[_dropIndexLen - 1]
      // 当前放置数据的父级
      let _currentDropDataParent = this.currentData
      let _currentDropData = null

      // 循环  dropIndexArray
      _dropIndexArray.forEach((item, index) => {
        if (index != _dropIndexLen - 1) {
          _currentDropDataParent = _currentDropDataParent[item].children

        } else {
          _currentDropData = _currentDropDataParent[item]
        }
      })
      // 删除拖动的数据
      const _currentDragData = dragDataSiblings.splice(dragIndex, 1)[0]
      // 根据目标最终位置，确定将数据加到哪里
      switch (this.targetPostion) {
        case 'middle':
          // const _nodeData = this.getTreeNodeData(_currentDropData.id)
          if (Artery.type(_currentDropData.children) != 'array') {
            this.$set(_currentDropData, 'children', [])
          }
           // 将删除的数据追加到对应的地方
          _currentDropData.children.push(_currentDragData)
          // _currentDropData.leaf = false
          break
        case 'top':
          // 将删除的数据追加到对应的地方
          _currentDropDataParent.splice(dropIndex, 0, _currentDragData)
          break
        case 'bottom':
          // 将删除的数据追加到对应的地方
          _currentDropDataParent.splice(dropIndex + 1, 0, _currentDragData)
          break
        default :
          break
      }
    },
    // 事件名
    emitEvent (eventName, event) {
      switch (eventName) {
        // 用户开始拖动元素时触发 （在拖动目标上触发事件 (源元素):）
        case 'dragstart':
          /**
           * #event
           *
           * @name    dragstart
           * @param   dragNode    被拖动的元素
           * @param   event       event对象
           * @body    console.log("开始拖动元素");
           * @description    用户开始拖动元素时触发
           */
          this.$emit('dragstart', this.dragNode, event)
          break
          // 元素正在拖动时触发 （在拖动目标上触发事件 (源元素):）
        case 'drag':
          /**
           * #event
           *
           * @name    drag
           * @param   dragNode      被拖动的元素
           * @param   event       event对象
           * @body    console.log("正在拖动");
           * @description    元素正在拖动时触发
           */
          this.$emit('drag', this.dragNode, event)
          break
          // 用户完成元素拖动后触发 （在拖动目标上触发事件 (源元素):）
        case 'dragend':
          /**
           * #event
           *
           * @name    dragend
           * @param   dragNode    被拖动的元素
           * @param   event       event对象
           * @body    console.log("拖动结束！");
           * @description    用户完成元素拖动后触发
           */
          this.$emit('dragend', this.dragNode, event)
          break
          // 当被鼠标拖动的对象进入其容器范围内时触发此事件  (释放目标时触发的事件:)
        case 'dragenter':
          /**
           * #event
           *
           * @name    dragenter
           * @param   enterNode    被鼠标拖动的对象进入时所在对象
           * @param   dragNode    被拖动的元素
           * @param   event       event对象
           * @body    console.log("dragenter!");
           * @description    当被鼠标拖动的对象进入其容器范围内时触发此事件
           */
          this.$emit('dragenter', this.hoverNode, this.dragNode, event)
          break
          // 当某被拖动的对象在另一对象容器范围内拖动时触发此事件  (释放目标时触发的事件:)
        case 'dragover':
          /**
           * #event
           *
           * @name    dragover
           * @param   overNode    被鼠标拖动的对象划过时所在对象
           * @param   dragNode    被拖动的元素
           * @param   event       event对象
           * @body    console.log("dragover")!
           * @description    当某被拖动的对象在另一对象容器范围内拖动时触发此事件
           */
          this.$emit('dragover', this.hoverNode, this.dragNode, event)
          break
          // 当被鼠标拖动的对象离开其容器范围内时触发此事件  (释放目标时触发的事件:)
        case 'dragleave':
          /**
           * #event
           *
           * @name    dragleave
           * @param   leaveNode    被鼠标拖动的对象离开时所在对象
           * @param   dragNode    被拖动的元素
           * @param   event       event对象
           * @body    console.log("dragleave")!
           * @description    当被鼠标拖动的对象离开其容器范围内时触发此事件
           */
          this.$emit('dragleave', this.hoverNode, this.dragNode, event)
          break

          // 在一个拖动过程中，释放鼠标键时触发此事件  (释放目标时触发的事件:)
        case 'drop':
          /**
           * #event
           *
           * @name    drop
           * @param   dropNode    被鼠标拖动的对象放置时所在对象
           * @param   dragNode    被拖动的元素
           * @param   targetPostion    被拖动的元素放置的位置，取值有 middle（内部） ,top（上方）， bottom（下方）
           * @param   event       event对象
           * @body     console.log("drop")!
           * @description    当被鼠标拖动的对象离开其容器范围内时触发此事件
           */
          this.$emit('drop', this.hoverNode, this.dragNode, this.targetPostion, event)
          break
        default :
          break
      }
    },
    getTreeNode(id) {
      if (!id) {
        return null
      }
      let treeNodes = this.$refs.treeNode
      if (!treeNodes) {
        return null
      }
      // if (!Array.isArray(treeNodes)) {
      //   treeNodes = [treeNodes]
      // }
      return this.recursiveFindTreeNode(treeNodes, id)
    },
    recursiveFindTreeNode(treeNodes, id) {
      if (treeNodes && treeNodes.length) {
        for (let i = 0; i < treeNodes.length; i++) {
          if (treeNodes[i].treeNodeData.id === id) {
            return treeNodes[i]
          }
          let treeNode = this.recursiveFindTreeNode(treeNodes[i].$children, id)
          if (treeNode) {
            return treeNode
          }
        }
      } else {
        return null
      }
    }
  },
  created () {
    // 这些数据不会做响应式, 所以放在这里去赋值, 这样Vue不会为他们生成getter和setter, 也不会监听他们的变化
    this.allTreeNodeData = {} // 这个由原来的缓存vue组件改为缓存数据, 并且只有can-search的情况才会用到它, 其余情况不存
    // this.currentCheckedNodes = {} // 这个暂时先不动, 目前还是缓存的tree-node vue组件 TODO 根据情况后续优化为treeNode 2019-11-12
    /**
     * ****************
     *
     * 关于拖拽的数据存储
     *
     * ****************
     */
    // 被拖动的节点
    this.dragNode = {}
    // 放置目标的节点
    this.dropNode = {}
    // 拖放中经过的节点
    this.hoverNode = {}
    // 是否正在拖拽
    this.dragging = false
    // 目标应该被放置的位置
    this.targetPostion = 'middle'//  top, bottom
    /*
     * 拖动
     * 被拖动元素预览图
     */
    this.treeDragPreview = {}
    // 被拖动元素预览图的style
    this.treeDragPreviewStyle = {}
    // 拖动的占位线
    this.treeDragLine = {}

    // 拖动的元素的内容展示
    this.treeDragContent = null

    // 源数据变动定时器（防抖函数）
    this.completeOriginDataTimer = null
    // 获得焦点的树节点
    this.focusTreeNode = null
  },
  mounted () {
    // 如果是可以拖动的
    if (this.draggable) {
      // 被拖动元素预览图
      this.treeDragPreview = this.$refs.jsTreeDragPreview
      // 拖动的占位线
      this.treeDragLine = this.$refs.jsTreeDragLine
      // 拖动的元素的内容展示
      this.treeDragContent = this.$refs.jsTreeDragContent
      // 将元素添加到body中
      document.body.appendChild(this.treeDragPreview)
      document.body.appendChild(this.treeDragLine)
      document.body.appendChild(this.treeDragContent)
    }
    /*
     * @time 2018-06-22
     * @description  暂时不需要选择激活，搜索改成了下拉的形式。后续可能根据实际需要添加。
     * // 如果是查询
     * if (this.canSearch) {
     * // 过滤条目
     * this.getFilterNodes(this.currentFilter);
     * }
     */
    // console.log(this.allTreeNodeData)
  },
  beforeDestroy () {
    // 如果是可以拖动的
    if (this.draggable) {
      // 移除body中添加的元素
      document.body.removeChild(this.treeDragPreview)
      document.body.removeChild(this.treeDragLine)
      document.body.removeChild(this.treeDragContent)
    }
  }
}
export default treeStore
